js 浏览器指纹信息相关

背景

近期的一个需求,app做第三方分享时,用微信,或QQ邀请好友,下载app,在被邀请方从分享页面获取app信息及下载app,安装,注册后,希望
可以关联到邀请方信息,可用于在后端统计,及被邀请方注册登录后,直接提示用户是否 添加 邀请方为好友。一般做法为生成邀请码,在注册时,用户填写邀请码,但这种方式,大多数用户不用在注册时,填写难记的邀请码信息。

与同事们讨论后,讨论了一种方案,第三方分享是H5页面,用户在点开分享页面时,记录用户的信息(网络,手机设备等),生成一个唯一标识与分享者uid关联。新用户在注册时,关联用户的机型,网络,关联到分享者信息。

js一般只能获取到浏览器的信息,浏览器的userAgent中可以获取部分设备信息(手机型号,品牌之类),虽然碰撞率很高,但目前也基本可以满足需求,最终浏览器指纹起到了一些辅助作用,也借此机会了解下相关概念,熟悉下js可以获取哪些用户信息,这里做个简单的记录

js指纹的概念理解

JS浏览器指纹的相关内容。浏览器指纹指的是通过JS采集用户的各种浏览器和设备信息,借此希望能生成相对唯一的uuid,用于区分用户

fingerprintjs2

Fingerprintjs2是github上开源的设备指纹采集器。最初的fingerprintjs库创建于2012年,但是由于新版本的开发很难保持向后兼容,因此Fingerprintjs2项目中增加了很多的新内容。

该项目将更多、更有效的来源用于指纹识别,并且可配置,也就是说用户可以选择性地开启其中的选项。该项目还将重点关注IE插件,尤其是在中国流行的QQ、Baidu等。另外,该项目使用了semver(语义化的版本控制系统)。

Fingerprintjs2的原理是根据所能获取到的设备相关信息:比如系统字体、ua、帆布指纹等数据,筛选计算出一个(94%情况下)近似唯一的24位哈希值,可以作为设备的唯一性标识。它的特点是不依赖cookie,用户无法通过清除缓存来删除。设备指纹现在被很多互联网反欺诈公司拿来作为用户特征识别的方法,也被一些广告公司用来跟踪用户数据。

Fingerprintjs2 源码列出目前可以采集到的信息

    keys = this.userAgentKey(keys);
    keys = this.languageKey(keys);
    keys = this.colorDepthKey(keys);
    keys = this.pixelRatioKey(keys);
    keys = this.hardwareConcurrencyKey(keys);
    keys = this.screenResolutionKey(keys);
    keys = this.availableScreenResolutionKey(keys);
    keys = this.timezoneOffsetKey(keys);
    keys = this.sessionStorageKey(keys);
    keys = this.localStorageKey(keys);
    keys = this.indexedDbKey(keys);
    keys = this.addBehaviorKey(keys);
    keys = this.openDatabaseKey(keys);
    keys = this.cpuClassKey(keys);
    keys = this.platformKey(keys);
    keys = this.doNotTrackKey(keys);
    keys = this.pluginsKey(keys);
    keys = this.canvasKey(keys);
    keys = this.webglKey(keys);
    keys = this.adBlockKey(keys);
    keys = this.hasLiedLanguagesKey(keys);
    keys = this.hasLiedResolutionKey(keys);
    keys = this.hasLiedOsKey(keys);
    keys = this.hasLiedBrowserKey(keys);
    keys = this.touchSupportKey(keys);
    keys = this.customEntropyFunction(keys);
    var that = this;

fingerprintjs的源码已经列出了目前能够收集到的几乎所有API,其中浏览器UA,语言,浏览器所在CPU等级(cpuclass,仅支持IE,chrome和firefox反回undefined),浏览器版本,等都是通过navigator对象获得,还有 window.indexedDBwindow.sessionStoragewindow.localStorage 这一类h5新增的特性是否存在也被用作区分值

Fingerprintjs2还考虑到了一些比较特殊的自定义区分值,比如时区,或者是否在浏览器层做了一些UA欺骗,比如 hasLiedLanguagesKey , HasLiedBrowser , HasLiedOs 。以 hasliedLanguageKey 为例,通过判断navigator.language和navigator.languages中第一个方法是否相同来实现:

try {
      var firstLanguages = navigator.languages[0].substr(0, 2);
      if(firstLanguages !== navigator.language.substr(0, 2)){
        return true;
      }
    } catch(err){
      return true;
    }

其中区分度最高的应该就是浏览器插件检测了,因为它还涉及到了一个插件安装顺序的唯独,也是通过navigator对象获得,需要对IE单独处理,关键代码:

var plugins = [];
      for(var i = 0, l = navigator.plugins.length; i < l; i++) {
        plugins.push(navigator.plugins[i]);
      }
      // sorting plugins only for those user agents, that we know randomize the plugins
      // every time we try to enumerate them
      if(this.pluginsShouldBeSorted()) {
        plugins = plugins.sort(function(a, b) {
          if(a.name > b.name){ return 1; }
          if(a.name < b.name){ return -1; }
          return 0;
        });
      }
      return this.map(plugins, function (p) {
        var mimeTypes = this.map(p, function(mt){
          return [mt.type, mt.suffixes].join("~");
        }).join(",");
        return [p.name, p.description, mimeTypes].join("::");
      }, this);

其他一些资源获取

检测字体

传统的方式是flash读取系统安装的字体,这里面还增加了一个通过css和js检测字体的方法,实现原理非常有趣。
以var text = ‘mmmmmmmmmmlli’;举例,不同的字体在同一大小的fontsize情况下,这段字体的长度和宽度是不同的,通过比较css的width和height,既可以判断用户是否有安装了相应的字体。实现的时候,可以在页面中动态插入一个span,讲fontsize设置为较大的值便于比较。这个手段也可以用在平常前端开发中,用以判断某种特定字体用户是否安装

Canvas 画布指纹

canvas是Html5中非常重要的一个特性,它同时也可以用语js指纹的一个重要熵值。其中的原因是因为同一个canvas图像在不同的设备上(不同的GPU),由于不同的渲染引擎、压缩比例,输出格式,以及同一系统字体安装不同,导致最后输出也不相同。通过在canvas画布上画字体,最后由toDataURL()方法专为64位格式。这一方法实现成本低,无法被禁用,立刻收到了广泛关注,目前这一方法被认为是浏览器指纹中十分重要的一种方法。不过在我的测试中,我发现还是很容易产生碰撞的,最简单的例子是在移动端上,比如高度统一的iPhone上面,同一代机型由于硬件相同,且ios在不越狱的情况下无法自定义字体,这就导致了的任意两台iphone6s通过safari判断画笔指纹会百分之百的获得同样的64位编码….

相关:

List of options

Advanced Tor Browser Fingerprinting